-module(ring).
-export([create/2,stop/1,init/1,test/0,
    ecrire/0,envoyer_ecrire/4,arreter/0,
    chercher/2,envoyer/3,parcourir/1,
    recuperer/2,delier/2,afficherFingerTable/1,
    creer_noeud/2,rejoindre/2,quitter/1,
    bin_to_hex/1,crasher/1,envoyerFichier/2,recupererFichier/3]).

bin_to_hex(Bin) when is_binary(Bin) ->
lists:flatten([integer_to_list(X,16) || <<X>> <= Bin]).


lookup(CleHachee,{Nom,MaCle,Next,CleSuivant,FingerTable},NT) ->
if
    MaCle == CleHachee ->
    Next;
    MaCle < CleSuivant ->
    if 
        ((MaCle =< CleHachee ) and (CleHachee < CleSuivant)) ->
        Next;
        true ->
        {T,Noeud} = rechercheFingerTable(CleHachee,FingerTable),
        if
            T==trouve ->
            Noeud;
            true ->
            envoyer_ecrire(NT,Nom,'lookup',Next),
            Next ! {lookup,CleHachee,Nom,NT},
            receive
                {ok_lookup,Hote} ->
                Hote
            end
        end
    end;
    MaCle >= CleSuivant -> %dernier
    if
        (MaCle =< CleHachee) or (CleHachee <CleSuivant) ->
        Next;
        true ->
        if
            CleHachee >= CleSuivant ->
            {T,Noeud} = rechercheFingerTable(CleHachee,FingerTable),
            if
                T==trouve ->
                Noeud;
                true ->
                envoyer_ecrire(NT,Nom,'lookup',Next),
                Next ! {lookup,CleHachee,Nom,NT},
                receive
                    {ok_lookup,Hote} ->
                    Hote
                end
            end
        end
    end
end.


get(CleHachee,Who,INFOS,NT) ->
Hote = lookup(CleHachee,INFOS,NT),
Nom = element(1,INFOS),
envoyer_ecrire(NT,Nom,'getdata',Hote),
Hote ! {getdata,CleHachee,Who,NT}.

getData(Map,Cle)->
X=dict:find(Cle,Map),
if
    X==error->
    Data=non_trouve;
    true ->
    {_,Data}=X
end,
Data.

put(CleHachee,Data,Who,INFOS,NT) ->
Hote = lookup(CleHachee,INFOS,NT),
Nom = element(1,INFOS),
envoyer_ecrire(NT,Nom,'putdata',Hote),
Hote ! {putdata,CleHachee,Data,Who,NT}.

putData(Map,Cle,Data)->
Map2 = dict:store(Cle,Data,Map),
Map2.

relier(X,Y) ->
X ! {next,self(),1},
receive {NX,_} ->
    Y ! {next,self(),1},
    receive {NY,_} ->
        Y ! {setNext,NX,self(),1},
        X ! {setNext,NY,self(),1},
        receive {ok,_} -> ok end,
        receive {ok,_} -> ok end,
        NY ! {setPred,X,self(),1},
        NX ! {setPred,Y,self(),1},
        receive {ok,_} -> ok end,
        receive {ok,_} -> ok end 
    end
end.

delier(X,Y) ->
Y ! {next,self(),1},
receive {NY,_} ->
    X ! {setNext,NY,self(),1},
    NY ! {setPred,X,self(),1},
    receive {ok,_} -> ok end,
    receive {ok,_} -> ok end,
    Y ! {setNext,Y,self(),1},
    Y ! {setPred,Y,self(),1},
    receive {ok,_} -> ok end,
    receive {ok,_} -> ok end 
end.

wait(Pred,Next,Node,T) ->
{Cle,{Nom,Noeud,Map,FingerTable,R}} = Node,
CleSuivant = crypto:sha(term_to_binary(Next)),%io:format("wait ~w~n",[Nom]),
INFOS = {Nom,Cle,Next,CleSuivant,FingerTable},

receive
    {next,Who,TT} ->
    NT = max(T,TT)+1,
    Who ! {Next,NT},
    envoyer_ecrire(NT,Node,'next',Who),
    wait(Pred,Next,Node,NT);

    {pred,Who,TT} ->
    NT = max(T,TT)+1,
    Who ! {Pred,NT},
    envoyer_ecrire(NT,Node,'pred',Who),
    wait(Pred,Next,Node,NT);

    {setNext,NNext,Who,TT} -> 
    NT = max(T,TT)+1,
    Who ! {ok,NT},
    envoyer_ecrire(NT,Node,'setNext',Who),
    wait(Pred,NNext,Node,NT); 

    {setPred,NPred,Who,TT} -> 
    NT = max(T,TT)+1,
    Who ! {ok,NT},
    envoyer_ecrire(NT,Node,'setPred',Who),
    wait(NPred,Next,Node,NT); 

    {nom, Who,TT} -> 
    NT = max(T,TT)+1,
    Who ! {Nom,NT},
    envoyer_ecrire(NT,Node,'nom',Who),
    wait(Pred,Next,Node,NT);

    {lookup,CleHachee,Who,TT} ->
    NT = max(T,TT)+1,

    NNext = testerSuivant(Pred,Next,Nom,NT),
    INFOS2 = {Nom,Cle,NNext,CleSuivant,FingerTable},

    Hote = lookup(CleHachee,INFOS2,NT),
    Who ! {ok_lookup,Hote},
    wait(Pred,NNext,Node,NT);

    {get,CleHachee,Who,TT} -> 
    NT = max(T,TT)+1,
    get(CleHachee,Who,INFOS,NT),
    wait(Pred,Next,Node,NT);

    {getdata,CleHachee,Who,TT} -> 
    NT = max(T,TT)+1,
    Data = getData(Map,CleHachee),
    Who ! {ok_get,Data},
    envoyer_ecrire(NT,Node,'ok_get',Who),
    wait(Pred,Next,Node,NT);

    {put,CleHachee,Data,Who,TT} ->
    NT = max(T,TT)+1,
    put(CleHachee,Data,Who,INFOS,NT),
    wait(Pred,Next,Node,NT);

    {copier,CleHachee,Data,K,Who,TT} ->
    NT = max(T,TT)+1,
    Map2 = putData(Map,CleHachee,Data),
    NNode = {Cle,{Nom,Noeud,Map2,FingerTable,K}},
    if
        Who==Nom ->
        ok;
        K==1 ->
        ok;  
        true ->
        envoyer_ecrire(NT,Nom,'copier',Next),
        Next ! {copier,CleHachee,Data,K-1,Who,NT}
    end,
    wait(Pred,Next,NNode,NT);

    {putdata,CleHachee,Data,Who,TT} ->
    NT = max(T,TT)+1,
    Map2 = putData(Map,CleHachee,Data),
    envoyer_ecrire(NT,Nom,'op_put',Who),
    Who ! {ok_put},
    NNode = {Cle,{Nom,Noeud,Map2,FingerTable,R}},
    envoyer_ecrire(NT,Nom,'copier',Next),
    Next ! {copier,CleHachee,Data,R,Nom,TT},
    wait(Pred,Next,NNode,NT);

    {cle,Who,TT} -> 
    NT = max(T,TT)+1,
    Who ! {Cle,NT},
    envoyer_ecrire(NT,Node,'cle',Who),
    wait(Pred,Next,Node,NT);  

    {cleSuivant, Who,TT} -> 
    NT = max(T,TT)+1,
    Who ! {CleSuivant,Nom,NT},
    envoyer_ecrire(NT,Node,'cleSuivant',Who),
    wait(Pred,Next,Node,NT);

    {getliste,Liste,Who,TT} -> 
    NT = max(T,TT)+1,
    case lists:keymember(Nom, 1, Liste) of
        true -> 
        envoyer_ecrire(NT,Nom,'ok_liste',Who),
        Who!{ok_liste,Liste},
        NNext = Next;
        false ->
        Elements = lists:map(fun({CleData,Data})-> {bin_to_hex(CleData),Data} end, lists:keysort(1,dict:to_list(Map))),
        Fingers = lists:map(fun({_,_,Finger})-> Finger end, FingerTable),
        Liste2=Liste++[{Nom,Elements,Fingers}],
        NNext = testerSuivant(Pred,Next,Nom,NT),
        envoyer_ecrire(NT,Nom,'getliste',NNext),
        NNext ! {getliste,Liste2,Who,NT}
    end,
    wait(Pred,NNext,Node,NT);

    {fingertable,Who,TT} ->
    NT = max(T,TT)+1,
    if
        length(FingerTable)==0 ->
        M=160,
        FingerTable2 = creerFingerTable(Cle,64,M,FingerTable,INFOS,NT),
        %io:format("~w]~n",[Nom]),
        %afficherFingerTable(FingerTable2),
        NNode = {Cle,{Nom,Noeud,Map,FingerTable2,R}},
        NNext = testerSuivant(Pred,Next,Nom,NT),
        envoyer_ecrire(NT,Nom,'fingertable',NNext),
        NNext ! {fingertable,Who,NT};
        true ->
        envoyer_ecrire(NT,Nom,'ok_fingertable',Who),
        Who ! ok_fingertable,
        NNode = Node,
        NNext = Next
    end,
    wait(Pred,NNext,NNode,NT);


 {join,Participant,Who,TT}->
    NT = max(T,TT)+1,
    envoyer_ecrire(NT,Nom,'lookup',Participant),
    Participant ! {lookup,Cle,Nom,NT},
    receive {ok_lookup,Hote}-> 
        Hote ! {pred,self(),NT},
        receive {P,_} ->
            NNext = Hote,
            envoyer_ecrire(NT,Nom,'setPred',Hote),
            Hote ! {setPred,Nom,self(),NT},
            receive {ok,_} -> ok end,

            P ! {setNext,Nom,self(),NT},
            envoyer_ecrire(NT,Nom,'setNext',P),
            receive {ok,_} -> ok end,
            NPred = P
        end
    end,
    envoyer_ecrire(NT,Nom,'updatedata',NNext),
    NNext ! {updatedata,Nom,NT},
    envoyer_ecrire(NT,Nom,'updatefingertab',NNext),
    NNext ! {updatefingertab,Who,NT},
    envoyer_ecrire(NT,Nom,'joined',Who),
    Who ! {joined,NT},
    wait(NPred,NNext,Node,NT);

    {leave,Who,TT}->
    NT = max(T,TT)+1,

    %enlever : tu donne tes données au suivant
    %et tu lui demande de les redistribuer

    Keys = dict:fetch_keys(Map),
    envoyerDonnees(Keys,Map,Next),
    Map2 = dict:new(),
    NNode = {Cle,{Nom,Noeud,Map2,[],R}},
    
    Pred ! {setNext,Next,self(),NT},
    envoyer_ecrire(NT,Nom,'setNext',Pred),
    Next ! {setPred,Pred,self(),NT},
    envoyer_ecrire(NT,Nom,'setPred',Next),
    receive {ok,_} -> ok end,
    receive {ok,_} -> ok end,
    NNext = Nom,
    NPred = Nom,

    envoyer_ecrire(NT,Nom,'leaved',Who),
    Who ! {leaved,NT},
    wait(NPred,NNext,NNode,NT);

    {updatefingertab,Who,TT} ->
    NT = max(T,TT)+1,
    if
        Who==Nom ->
        FingerTable2 = FingerTable,
        NNext = Next;
        true->

        NNext = testerSuivant(Pred,Next,Nom,NT),
        INFOS2 = {Nom,Cle,NNext,CleSuivant,FingerTable},

        FingerTable2 = updateFingerTab(FingerTable,INFOS2,NT),
        envoyer_ecrire(NT,Nom,'updatefingertab',NNext),
        NNext ! {updatefingertab,Nom,NT}
    end,
    NNode = {Cle,{Nom,Noeud,Map,FingerTable2}},
    wait(Pred,NNext,NNode,NT);

    {updatedata,Who,TT} ->
    NT = max(T,TT)+1,
    Keys = dict:fetch_keys(Map),
    NMap = updateData(Keys,Map,Nom,INFOS,NT),
    NNode = {Cle,{Nom,Noeud,NMap,FingerTable,R}},
    envoyer_ecrire(NT,Nom,'ok_updatedata',Who),
    Who ! {ok_updatedata,NT},
    wait(Pred,Next,NNode,NT);

    {trouver_dernier,Who,TT}->
    NT = max(T,TT)+1,
    io:format("2~w]~w~n",[Nom,Pred]),
    case lists:member(Pred,registered()) of
        false -> 
        envoyer_ecrire(NT,Nom,'dernier',Who),
        Who ! {dernier,Nom,NT};
        true ->
        envoyer_ecrire(NT,Nom,'trouver_dernier',Pred),
        Pred ! {trouver_dernier,Who,NT}
    end,
    wait(Pred,Next,Node,NT);

    {putfile,NomFichier,Who,TT}->
    NT = max(T,TT)+1,
    case file:read_file_info(NomFichier) of
        {ok,FileInfo} ->
        Taille = element(2,FileInfo),
        TailleKO = Taille/1024,
        io:format("~wko~n",[TailleKO]),

        if
            TailleKO<64 ->
            case file:read_file(NomFichier) of
                {ok,ContenuFichier} ->
                Data = {fichier,NomFichier,ContenuFichier},
                CleHachee = crypto:sha(list_to_binary(NomFichier)),
                envoyer_ecrire(NT,Nom,'put',Next),
                Next !  {put,CleHachee,Data,Who,NT};
                {error,_} ->
                io:format("impossible de lire ~w~n",[NomFichier])
            end;
            true ->
            NombreParties = trunc(TailleKO/64)+1,
            io:format("~w parties~n",[NombreParties]),
            Metas = listeMetas(NomFichier,NombreParties),
            Data = {metas,NomFichier,Metas},
            CleHachee = crypto:sha(list_to_binary(NomFichier)),
            envoyer_ecrire(NT,Nom,'put',Next),
            Next ! {put,CleHachee,Data,Who,NT},
            case file:open(NomFichier, read) of
                {ok, IoDevice} ->
                envoyerMetas(Next,Metas,IoDevice);
                {error, _} ->
                io:format("Impossible d'ouvrir ~w~n",[NomFichier])
            end
        end;
        {error, _} ->
        io:format("Fichier introuvable~n")
    end,
    wait(Pred,Next,Node,NT);

    {getfile,NomFichier,NomFichierSortie,Who,TT}->
    io:format("GETFILE~n"),
    NT = max(T,TT)+1,
    CleHachee = crypto:sha(list_to_binary(NomFichier)),
    Hote = lookup(CleHachee,INFOS,NT),
    io:format("LOOKUP~n"),
    if
        Hote == Nom ->
        Data = getData(Map,CleHachee);
        true ->
        envoyer_ecrire(NT,Nom,'getdata',Hote),
        Hote ! {getdata,CleHachee,Nom,NT},
        receive
            {ok_get,D} ->
            Data = D
        end
    end,
    io:format("FIN LOOKUP~n"),
    case file:open(NomFichierSortie,append) of
        {ok, File}->
        io:format("Fichier ~w ouvert en lecture~n",[NomFichier]),
        case Data of
            {fichier,_,ContenuFichier} ->
            file:write(File,ContenuFichier),
            envoyer_ecrire(NT,Nom,'ok_getfile',Who),
            Who ! {ok_getfile};
            {metas,_,Metas} ->
            io:format("taille : ~w~n",[length(Metas)]),
            recupererFichier([Metas],File,INFOS,Who,NT)
        end;
        {error, Reason}->
        io:format("Impossible d'ouvrir en ecriture~n~w~n~w",[NomFichierSortie,Reason])
    end,
    wait(Pred,Next,Node,NT);

    stop -> finit
end.

recupererFichier([],File,INFOS,Who,NT) ->
file:close(File),
Nom = element(1,INFOS),
envoyer_ecrire(NT,Nom,'ok_getfile',Who),
Who ! {ok_getfile};

recupererFichier([Meta|Reste],File,INFOS,Who,NT) ->
{Nom,_,_,Map,_} = INFOS,
CleHachee = crypto:sha(list_to_binary(Meta)),
Hote = lookup(CleHachee,INFOS,NT),
if
 Hote == Nom ->
 Data = getData(Map,CleHachee);
 true ->
 envoyer_ecrire(NT,Nom,'getdata',Hote),
 Hote ! {getdata,CleHachee,Nom,0},
 receive
    {ok_get,D} ->
    Data = D
end
end,
file:write(File,Data),
recupererFichier(Reste,File,INFOS,Who,NT).

listeMetas(NomFichier,0)->
[string:concat(NomFichier, integer_to_list(0))];
listeMetas(NomFichier,NB)->
[string:concat(NomFichier, integer_to_list(NB))]
++
listeMetas(NomFichier,(NB-1)).

envoyerMetas(_,[],_)->ok;
envoyerMetas(Next,[Meta|Reste],File)->
CleHachee = crypto:sha(list_to_binary(Meta)),
case file:read(File, 64) of
    {ok, Data} ->
    Next ! {put,CleHachee,Data,self(),0},
    envoyerMetas(Next,Reste,File);
    % case file:position(File, {cur,64}) of
    %     {ok, _} -> 
    %     {error, _} -> error
    % end;
    eof -> ok;
    {error, _} -> error
end.



testerSuivant(Pred,Next,Nom,NT)->
case lists:member(Next,registered()) of
    false ->
    envoyer_ecrire(NT,Nom,'trouver_dernier',Pred),
    Pred ! {trouver_dernier,Nom,NT}, 
    receive
        {dernier,Dernier,_} ->
        NNext = Dernier,
        envoyer_ecrire(NT,Nom,'setPred',Dernier),
        Dernier ! {setpred,Nom,NT}
    end;
    true ->
    NNext = Next
end,
NNext.

creerFingerTable(Cle,K,M,FingerTable,INFOS,NT) ->
if 
    K>=2 -> 
    N = binary:decode_unsigned(Cle,big),
    A = calculStart(N,K,M),
    B = calculStart(N,K/2,M),
    StartActuel  = binary:encode_unsigned(A,big),
    StartSuivant = binary:encode_unsigned(B,big),

    Interval = [StartActuel,StartSuivant],

    Node = lookup(StartActuel,INFOS,NT),
    Finger = [{StartActuel,Interval,Node}],
    Finger ++ creerFingerTable(Cle,K/2,M,FingerTable,INFOS,NT);
    true ->
    FingerTable
end.

calculStart(N,K,M) ->
DM = round(math:pow(2,M)),
A = (N+round((1/K)*DM)),

Start = A rem DM,
Start.


rechercheFingerTable(_,[]) ->
{nontrouve,nontrouve};
rechercheFingerTable(Cle,[Ligne|Reste]) ->
{T,Noeud} = rechercheFingerTableLigne(Cle,Ligne),
if
    T==trouve ->
    {trouve,Noeud};
    true->
    rechercheFingerTable(Cle,Reste)
end.

rechercheFingerTableLigne(Cle,{_,[Deb,Fin],Noeud}) ->
if
    (Deb=<Cle) and (Cle<Fin) ->
    {trouve,Noeud
    };
    true->
    {nontrouve,nontrouve}
end.

updateFingerTab([],_,_)->[];
updateFingerTab([Ligne|Reste],INFOS,NT) ->
Ligne2=updateFingerTabLigne(Ligne,INFOS,NT),
Reste2=updateFingerTab(Reste,INFOS,NT),
[Ligne2]++Reste2.

updateFingerTabLigne({Start,Interval,_},INFOS,NT)->
Node=lookup(Start,INFOS,NT),
{Start,Interval,Node}.

updateData([],Map,_,_,_) -> Map;
updateData([Cle|Reste],Map,Nom,INFOS,NT) -> 
H = lookup(Cle,INFOS,NT),
if 
    H==Nom ->
    ok;
    true ->
    Data = getData(Map,Cle),
    H ! {putdata,Cle,Data,self(),NT},
    receive 
        {ok_put} ->
        ok
    end
end,
updateData(Reste,Map,Nom,INFOS,NT).

envoyerDonnees([],_,_)-> ok;
envoyerDonnees([Cle|Reste],Map,Next) ->
Data = getData(Map,Cle),
Next ! {putdata,Cle,Data,self(),0},
envoyerDonnees(Reste,Map,Next).

afficherFingerTable([])-> ok;
afficherFingerTable([{Start,Interval,Who}|Reste])->
io:format("S: ~w~n",[Start]),
io:format("I: ~w~n",[Interval]),
io:format("P:~w~n",[Who]),
afficherFingerTable(Reste).


envoyer_ecrire(Temps,Nom,Message,Dest) -> 
Cle = crypto:sha(term_to_binary(Nom)),
CleSuivant = crypto:sha(term_to_binary(Dest)),
ecrivain ! {Temps,{Nom,Cle},Message,{Dest,CleSuivant}}.

ecrire()->
receive
    {Temps,{Pr1,Hach1},Message,{Pr2,Hach2}} ->
    file:write_file("log.txt", io_lib:fwrite("~w) ~p) ~w]~n <~w,~w> ~n --~w--> ~n <~w,~w> ~n~n", 
        [date(),time(),Temps,Pr1,binary:decode_unsigned(Hach1,big),Message,Pr2,binary:decode_unsigned(Hach2,big)]),[append]),
    ecrire();
    stop_ecrire -> {}
end.

creer_ecrivain() ->
Pid = spawn(?MODULE,ecrire,[]),
file:write_file("log.txt", io_lib:fwrite("",[])),
register(ecrivain, Pid).

create(L,R) ->
creer_ecrivain(),
N = lists:map(fun(X)-> {crypto:sha(term_to_binary(X)),{X,node(),dict:new(),[],R}} end, L),
NT = lists:keysort(1,N),
{_,[Tete|_]} = pop(NT),
NO = lists:map(fun({_,{Nom,Noeud,Map,FingerTable,R1}}) -> {Nom,Noeud,Map,FingerTable,R1} end, NT),

afficher(NO),
{Tete,NO}.

creer_noeud(Nom,K)->
Noeud = {crypto:sha(term_to_binary(Nom)),{Nom,node(),dict:new(),[],K}},
popNode(Noeud).

stop(N) ->
N ! stop,
ecrivain ! stop_ecrire.

init(Node) ->
{_,{Nom,_,_,_,_}} = Node,
wait(Nom,Nom,Node,0).

popNode(Node) ->
{_,{Nom,_,_,_,_}} = Node,
Pid = spawn(?MODULE,init,[Node]),
register(Nom, Pid),
{Nom,[Nom]}.

pop([Node|[]]) ->
{P,A} = popNode(Node),
{P,A};
pop([Node|R]) ->
{P,A} = popNode(Node),
{Q,B} = pop(R),
relier(P,Q),
{Q, A++B}
.

afficher({_,{Nom,Node,_,_,_}}) ->
io:format("Node{Nom(~w) Node(~w) Cle(...)} ~n",[Nom,Node]);
afficher({Nom,Node,_,_,_}) ->
io:format("Node{Nom(~w) Node(~w)} ~n",[Nom,Node]);
afficher([]) -> {};
afficher([Node|R]) ->
afficher(Node),
afficher(R).

test() ->
L = [toto,tata,bob,anna,lea],
R = 3,
{Tete,_}=create(L,R),
fingerTable(Tete),
envoyerFichier(bob,"test.html"),
recupererFichier(bob,"test.html","test2.html").
% envoyer(Tete,aaaa,a),
% envoyer(Tete,bbbb,b),
% envoyer(Tete,cccc,c),
% envoyer(Tete,dddd,d),
% envoyer(Tete,eeee,e),
% envoyer(Tete,ffff,f),
% envoyer(Tete,gggg,g),
% envoyer(Tete,hhhh,h),
% envoyer(Tete,iiii,i),
% envoyer(Tete,jjjj,j),
% envoyer(Tete,kkkk,k),
% recuperer(Tete,aaaa).

arreter() ->
stop(toto).

chercher(Noeud,Nom) ->
Noeud ! {lookup,crypto:sha(term_to_binary(Nom)),self(),0},
receive
    {ok_lookup,Hote} ->
    Hote
end.

envoyer(Noeud,Cle,Data) ->
Noeud ! {put,crypto:sha(term_to_binary(Cle)),Data,self(),0},
receive
    {ok_put} ->
    ok
end.

recuperer(Noeud,Cle) ->
Noeud ! {get,crypto:sha(term_to_binary(Cle)),self(),0},
receive
    {ok_get,Data} ->
    Data
end.

fingerTable(Noeud) ->
Noeud ! {fingertable,self(),0},
receive
    ok_fingertable -> ok
end.

rejoindre(Noeud,Participant) ->
Noeud ! {join,Participant,self(),0},
receive
    {joined,_} -> ok
end.

quitter(Noeud) ->
Noeud ! {leave,self(),0},
receive
    {leaved,_} -> ok
end.

crasher(Noeud) ->
unregister(Noeud).

parcourir(Noeud)->
Noeud ! {getliste,[],self(),0},
receive
  {ok_liste,ListeRecue} -> 
   %afficherRing(ListeRecue),
   {server,'server@PC'} ! {self(), ListeRecue}, ok
end.

envoyerFichier(Noeud,NomFichier) ->
Noeud ! {putfile,NomFichier,self(),0}.

recupererFichier(Noeud,NomFichier,NomFichierSortie) ->
Noeud ! {getfile,NomFichier,NomFichierSortie,self(),0}.